home *** CD-ROM | disk | FTP | other *** search
- /*
- File: MoreDisks.c
-
- Contains: General disk driver utility routines.
-
- Written by: Quinn
-
- Copyright: Copyright © 1999 by Apple Computer, Inc., all rights reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- Change History (most recent first):
-
- <2> 7/5/99 Quinn Added MoreIsDriveCDROM. Fixed bug in MoreGetDriveRefNum where
- it was calling MoreUTFindDrive rather than MoreUTFindDriveQ, and
- hence failing on disks controlled by foreign file systems.
- <1> 16/3/99 Quinn First checked in.
- */
-
- /////////////////////////////////////////////////////////////////
-
- // MoreIsBetter Setup
-
- #include "MoreSetup.h"
-
- // Mac OS Interfaces
-
- #include <Devices.h>
- #include <DriverGestalt.h>
- #include <Gestalt.h>
- #include <FSM.h>
- #include <Disks.h>
- #include <StringCompare.h>
-
- // MIB Prototypes
-
- #include "TradDriverLoaderLib.h"
- #include "MoreInterfaceLib.h"
- #include "MoreMemory.h"
-
- // Our Prototypes
-
- #include "MoreDisks.h"
-
- /////////////////////////////////////////////////////////////////
- #pragma mark ----- Basic Disk Driver Utilities -----
-
- extern pascal DriveFlagsPtr MoreGetDriveFlags(DrvQElPtr drvQEl)
- // See comment in interface part.
- {
- MoreAssertQ(drvQEl != nil);
-
- return ((DriveFlagsPtr) drvQEl) - 1;
- }
-
- extern pascal OSErr MoreUTFindDriveQ(SInt16 drive, DrvQElPtr *foundDrvQEl)
- // See comment in interface part.
- {
- OSErr err;
- UInt32 fsmVers;
-
- MoreAssertQ(drive > 0);
- MoreAssertQ(foundDrvQEl != nil);
-
- // Check to see whether we have a useful version of FSM. Versions of FSM
- // prior to 1.2 do not support the documented FSM API, so we just treat
- // them as if FSM wasn't installed.
-
- if ((Gestalt(gestaltFSMVersion, (SInt32 *) &fsmVers) == noErr) && (fsmVers >= 0x0120)) {
-
- // We have FSM, let's call its version of UTFindDrive,
- // and handle the weirdo error we get for non-HFS disks.
-
- err = MoreUTFindDrive(drive, foundDrvQEl);
- if (err == extFSErr) {
- err = noErr;
- }
- } else {
- DrvQElPtr thisDrv;
-
- // No FSM, let's go poking around in low memory )-:
-
- *foundDrvQEl = nil;
-
- thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead;
- while (thisDrv != nil && *foundDrvQEl == nil) {
- if (thisDrv->dQDrive == drive) {
- *foundDrvQEl = thisDrv;
- } else {
- thisDrv = (DrvQElPtr) thisDrv->qLink;
- }
- }
- if (*foundDrvQEl == nil) {
- err = nsDrvErr;
- } else {
- err = noErr;
- }
- }
-
- return err;
- }
-
- extern pascal DrvQElPtr MoreGetIndDrive(SInt16 index)
- // See comment in interface part.
- {
- DrvQElPtr thisDrv;
- SInt16 thisDrvIndex;
-
- MoreAssertQ(index > 0);
-
- thisDrvIndex = 1;
- thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead;
- while (thisDrv != nil && thisDrvIndex != index) {
- thisDrvIndex += 1;
- thisDrv = (DrvQElPtr) thisDrv->qLink;
- }
- return thisDrv;
- }
-
- extern pascal SInt16 MoreFindFreeDriveNumber(SInt16 firstDrive)
- // See comment in interface part.
- {
- SInt16 candidate;
- DrvQElPtr junkDrvQElPtr;
-
- MoreAssertQ(firstDrive >= 5);
-
- candidate = firstDrive;
- while ( MoreUTFindDriveQ(candidate, &junkDrvQElPtr) == noErr ) {
- candidate += 1;
- }
-
- // This post condition checks that we didn't wrap
- // around to a negative drive number.
-
- MoreAssertQ(candidate >= 5);
-
- return candidate;
- }
-
- extern pascal OSErr MoreRemoveDrive(DrvQElPtr drvQEl)
- // See comment in interface part.
- {
- OSStatus err;
-
- if ( MoreVolumeMountedOnDrive(drvQEl->dQDrive, false) == 0 ) {
- err = Dequeue( (QElemPtr) drvQEl, GetDrvQHdr());
- } else {
- err = volOnLinErr;
- }
- return err;
- }
-
- extern pascal DriverRefNum MoreGetDriveRefNum(SInt16 drive)
- // See comment in interface part.
- {
- DrvQElPtr foundDrvQEl;
-
- MoreAssertQ(drive > 0);
-
- if ( MoreUTFindDriveQ(drive, &foundDrvQEl) == noErr) {
- return foundDrvQEl->dQRefNum;
- } else {
- return 0;
- }
- }
-
- static pascal Boolean MoreDriveSupportsDriverGestaltInternal(DriverRefNum refNum)
- // An internal version of MoreDriveSupportsDriverGestalt that allows
- // you to pass in the refNum and the drive number. You can pass
- // in 0 for either refNum or drive (but not both) and the routine
- // will do the appropriate mapping.
- {
- OSErr junk;
- DriverFlags driverFlags;
-
- junk = TradGetDriverInformation(refNum, nil, &driverFlags, nil, nil);
- MoreAssertQ(junk == noErr);
- return TradDriverGestaltIsOn(driverFlags);
- }
-
- extern pascal Boolean MoreDriveSupportsDriverGestalt(SInt16 drive)
- // See comment in interface part.
- {
- return MoreDriveSupportsDriverGestaltInternal(MoreGetDriveRefNum(drive));
- }
-
- static pascal Boolean MoreDriveSupportFileExchangeInternal(DriverRefNum refNum, SInt16 drive)
- // An internal version of MoreDriveSupportFileExchange that allows
- // you to pass in the refNum and the drive number. You can pass
- // in 0 for either refNum or drive (but not both) and the routine
- // will do the appropriate mapping.
- {
- DriverGestaltParam pb;
- Boolean result;
-
- MoreAssertQ( (refNum < 0 && drive >= 0 && (drive == 0 || MoreGetDriveRefNum(drive) == refNum)) ||
- (refNum == 0 && drive > 0) );
-
- if (refNum == 0) {
- refNum = MoreGetDriveRefNum(drive);
- }
-
- result = false;
- if ( MoreDriveSupportsDriverGestaltInternal(refNum) ) {
- pb.ioVRefNum = drive;
- pb.ioCRefNum = refNum;
- pb.csCode = kDriverGestaltCode;
- pb.driverGestaltSelector = kdgAPI;
-
- if ( PBStatusSync((ParmBlkPtr) &pb) == noErr
- && GetDriverGestaltAPIResponse(&pb)->partitionCmds & 0x01 ) {
- result = true;
- }
- }
- return result;
- }
-
- extern pascal Boolean MoreDriveSupportFileExchange(SInt16 drive)
- // See comment in interface part.
- {
- MoreAssertQ(drive > 0);
- return MoreDriveSupportFileExchangeInternal(0, drive);
- }
-
- // This is the number of format list entries we allocate when issuing
- // the return format list status call. There's no way we can calculate
- // the "correct" number, but this should be more than enough.
-
- enum {
- kFormatListEntryCount = 16
- };
-
- extern pascal OSErr MoreGetDriveSize(SInt16 drive, UInt32 *sizeInBlocks)
- // See comment in interface part.
- {
- OSErr err;
- DrvQElPtr drvQEl;
- CntrlParam pb;
- FormatListRec formatList[kFormatListEntryCount];
- SInt16 formatIndex;
- Boolean foundFormat;
- Str255 driverName;
- DrvSts status;
-
- MoreAssertQ(drive > 0);
- MoreAssertQ(sizeInBlocks != nil);
-
- // Start by finding the drive queue element for
- // the drive, and by making sure that there's a disk
- // in the drive.
-
- err = MoreUTFindDriveQ(drive, &drvQEl);
- if (err == noErr) {
- if ( MoreGetDriveFlags(drvQEl)->diskInPlace <= 0 ) {
- err = offLinErr;
- }
- }
-
- // Wow, this is harder than it should be, all because of the
- // silly ".Sony" driver. The basic problem is that
- // the ".Sony" driver doesn't store the disk size in the
- // drive queue element like every other disk drive on the
- // planet. The solution is a three step process as described
- // in the comments below.
-
- if (err == noErr) {
-
- // Step 1. If the driver supports the kReturnFormatList status call,
- // use it to get a list of formats for the drive and then
- // return the format marked as current.
-
- pb.ioNamePtr = nil;
- pb.ioVRefNum = drvQEl->dQDrive;
- pb.ioCRefNum = drvQEl->dQRefNum;
- pb.csCode = kReturnFormatList;
- pb.csParam[0] = kFormatListEntryCount;
- *((FormatListRec **) &pb.csParam[1]) = formatList;
- err = PBStatusSync( (ParmBlkPtr) &pb);
- if (err == noErr) {
- foundFormat = false;
- for (formatIndex = 0; formatIndex < pb.csParam[0]; formatIndex++) {
- if ((formatList[formatIndex].formatFlags & diCIFmtFlagsCurrentMask) != 0) {
- *sizeInBlocks = formatList[formatIndex].volSize;
- foundFormat = true;
- }
- }
- if ( ! foundFormat ) {
-
- // Hmmm, this isn't good. The disk driver returned a format
- // list but none of the formats were marked as "current".
- // We handle this correctly but, in debug builds, we'll also
- // drop into MacsBug, just to let you know this is happening.
-
- MoreAssertQ(false);
- err = paramErr;
- }
- } else {
-
- // ••• The logic here is slightly screwed up. The problem is that
- // I can't tell whether the kReturnFormatList call failed because
- // the driver just doesn't support it, or because the driver failed
- // to get the information for some other reason. If that driver
- // happens to be the ".Sony" driver, I'm going to take a wrong step
- // next.
- //
- // For example, say that there's a 1.4MB disk in the floppy drive
- // and I call kReturnFormatList and it fails with an error because
- // of a cosmic ray. I then test the driver name, find that it's
- // ".Sony", call DriveStatus, and then return noErr with a size
- // of either 400KB or 800KB. Not good.
- //
- // You might think this is an unlikely occurence, but it's exactly
- // what happens when there's no disk in the floppy drive. I've
- // special-cased that away above, but the general problem still stands.
- //
- // What are the alternatives? I could special case the error
- // result from kReturnFormatList and only run this code if I get
- // statusErr. But can you guarantee that all ".Sony" drives
- // return statusErr for an unrecognised status code? I thought not.
- // Beyond that, I can't think of any options. So this code
- // stands. It's probably never going to bite anyone, but it's
- // worth noting here, just in case. Besides, this is what
- // the equivalent routine in MoreFiles does (-:
- //
- // -- Quinn, 3 Mar 1999
-
- // Step 2. If that doesn't work, then look at the driver. If it's
- // the ".Sony" driver (and this will be a really old ".Sony" driver
- // because new ones support kReturnFormatList), special case
- // the possible media types.
-
- err = TradGetDriverInformation(drvQEl->dQRefNum, nil, nil, driverName, nil);
- if (err == noErr) {
- if ( EqualString(driverName, "\p.Sony", false, true) ) {
- err = DriveStatus(drvQEl->dQDrive, &status);
- if (err == noErr) {
- if ( status.twoSideFmt == 0 ) {
- *sizeInBlocks = 400 * 2;
- } else {
- *sizeInBlocks = 800 * 2;
- }
- }
- } else {
-
- // Step 3. If it's not the ".Sony" driver, get the size out of the
- // drive queue element.
-
- if (drvQEl->qType == 0) {
-
- // Old style drive, with just 16 bits of size information
- // in dQDrvSz.
-
- *sizeInBlocks = drvQEl->dQDrvSz;
- } else {
-
- // New style drive, with 32 bits of size information spread
- // between dQDrvSz and dQDrvSz2.
-
- *sizeInBlocks = (((UInt32 ) drvQEl->dQDrvSz2) * 65536) + (UInt32 ) drvQEl->dQDrvSz;
- }
- }
- }
- }
- }
-
- return err;
- }
-
- extern pascal SInt16 MoreVolumeMountedOnDrive(SInt16 drive, Boolean ejectedIsMounted)
- // See comment in interface part.
- {
- SInt16 result;
- VCBPtr thisVCB;
-
- MoreAssertQ(drive > 0);
-
- // Get the VCB queue (in low memory) and walk it.
- // We can't use UTLocateNextVCB because it will only
- // iterate volumes by name, not return a complete list.
-
- result = 0;
- thisVCB = (VCBPtr) GetVCBQHdr()->qHead;
- while (thisVCB != nil && result == 0) {
- if (thisVCB->vcbDrvNum == drive ||
- (ejectedIsMounted &&
- thisVCB->vcbDrvNum == 0 &&
- thisVCB->vcbDRefNum == drive
- )
- ) {
- MoreAssertQ(thisVCB->vcbDrvNum == 0 || thisVCB->vcbDRefNum == MoreGetDriveRefNum(drive));
- result = thisVCB->vcbVRefNum;
- } else {
- thisVCB = (VCBPtr) thisVCB->qLink;
- }
- }
-
- return result;
- }
-
- extern pascal SInt16 MoreFirstDriveWithoutVolume(DriverRefNum refNum)
- // See comment in interface part.
- {
- Boolean found;
- DrvQElPtr thisDrv;
-
- found = false;
- thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead;
- while (thisDrv != nil && ! found) {
- if (thisDrv->dQRefNum == refNum && MoreVolumeMountedOnDrive(thisDrv->dQDrive, false) == 0) {
- found = true;
- } else {
- thisDrv = (DrvQElPtr) thisDrv->qLink;
- }
- }
- if (found) {
- return thisDrv->dQDrive;
- } else {
- return 0;
- }
- }
-
- extern pascal void MoreIsDriveCDROM(SInt16 drive, MoreDisksCDROMResponse *response)
- // See comment in interface part.
- {
- DriverRefNum refNum;
- DriverFlags drvrFlags;
- Str255 drvrName;
- DriverGestaltParam pb;
-
- MoreAssertQ(drive > 0);
- MoreAssertQ(response != nil);
-
- *response = kMoreDriveUnableToDetermineCDROM;
-
- refNum = MoreGetDriveRefNum(drive);
- if (refNum != 0) {
- if ( TradGetDriverInformation(refNum, nil, &drvrFlags, drvrName, nil) == noErr ) {
-
- // Step 1 -- if the driver supports driver gestalt, we
- // exclusively rely on its response for the kdgDeviceType
- // selector.
-
- if ( TradDriverGestaltIsOn(drvrFlags) ) {
- pb.ioVRefNum = drive;
- pb.ioCRefNum = refNum;
- pb.csCode = kDriverGestaltCode;
- pb.driverGestaltSelector = kdgDeviceType;
-
- if ( PBStatusSync((ParmBlkPtr) &pb) == noErr ) {
- if (GetDriverGestaltDevTResponse(&pb)->deviceType == kdgCDType) {
- *response = kMoreDriveIsCDROM;
- } else {
- *response = kMoreDriveIsNotCDROM;
- }
- }
- }
-
- // Step 2 -- if the above didn't work, we only say it's a CD-ROM
- // if the driver is ".AppleCD".
-
- if (*response == kMoreDriveUnableToDetermineCDROM) {
- if ( EqualString(drvrName, "\p.AppleCD", false, true) ) {
- *response = kMoreDriveIsCDROM;
- } else if ( EqualString(drvrName, "\p.AFPTranslator", false, true) ) {
- // ".AFPTranslator" does not respond to Driver Gestalt,
- // which is pretty lame IMHO. [Radar ID ] Regardless,
- // we know it's not a CD-ROM.
- *response = kMoreDriveIsNotCDROM;
- } else if ( EqualString(drvrName, "\p.Sony", false, true) ) {
- *response = kMoreDriveIsNotCDROM;
- }
- }
- }
- }
- }
-
- /////////////////////////////////////////////////////////////////
- #pragma mark ----- File Exchange Control Call Interface -----
-
- extern pascal OSErr MoreCreateNewDriveQueueElement(SInt16 driveToClone,
- UInt32 firstBlock, UInt32 sizeInBlocks,
- SInt16 *newDrive)
- // See comment in interface part.
- {
- OSErr err;
- OSErr junk;
- CntrlParam pb;
- DrvQElPtr drvQEl;
-
- MoreAssertQ(driveToClone > 0);
- MoreAssertQ(newDrive != nil);
-
- // First check that the driver supports the File Exchange
- // control call interface.
-
- err = noErr;
- if ( ! MoreDriveSupportFileExchange(driveToClone) ) {
- err = controlErr;
- }
-
- // Find the drive queue element associated with
- // driveToClone. This is an input parameter to
- // kGetADrive.
-
- if (err == noErr) {
- err = MoreUTFindDriveQ(driveToClone, &drvQEl);
- }
-
- // Make the kGetADrive call to the driver. Because
- // we pass a pointer to memory outside of the parameter
- // block (drvQEl) and the driver might be a paging device,
- // we must hold drvQEl (and make sure to unhold it later!).
-
- if (err == noErr) {
- err = SafeHoldMemory(&drvQEl, sizeof(drvQEl));
- if (err == noErr) {
- pb.ioVRefNum = driveToClone;
- pb.ioCRefNum = MoreGetDriveRefNum(driveToClone);
- pb.csCode = kGetADrive;
- *((DrvQElPtr **) &pb.csParam[0]) = &drvQEl;
-
- err = PBControlSync((ParmBlkPtr) &pb);
- if (err == noErr) {
- *newDrive = drvQEl->dQDrive;
- }
- junk = SafeUnholdMemory(&drvQEl, sizeof(drvQEl));
- MoreAssertQ(junk == noErr);
- }
- }
-
- // Now re-target the new drive to the partition on the
- // disk specified by firstBlock and sizeInBlocks. We do
- // this in the create call because some disk drivers
- // don't always inherit the partition information from
- // the drive that was cloned.
-
- if (err == noErr) {
- err = MoreSetDrivePartition(*newDrive, firstBlock, sizeInBlocks);
- }
-
- return err;
- }
-
- extern pascal OSErr MoreSetDrivePartition(SInt16 drive, UInt32 firstBlock, UInt32 sizeInBlocks)
- // See comment in interface part.
- {
- OSErr err;
- CntrlParam pb;
- DrvQElPtr drvQEl;
-
- MoreAssertQ(drive > 0);
-
- // First check that the driver supports the File Exchange
- // control call interface.
-
- err = noErr;
- if ( ! MoreDriveSupportFileExchange(drive) ) {
- err = controlErr;
- }
-
- // Find the drive queue element associated with
- // drive. This is an input parameter to
- // kRegisterPartition.
-
- if (err == noErr) {
- err = MoreUTFindDriveQ(drive, &drvQEl);
- }
-
- // Make the kRegisterPartition control call. We
- // don't need to hold any memory because all the
- // parameters to this control call are entirely
- // contained within the parameter block.
-
- if (err == noErr) {
- pb.ioVRefNum = drive;
- pb.ioCRefNum = MoreGetDriveRefNum(drive);
- pb.csCode = kRegisterPartition;
- *((DrvQElPtr *) &pb.csParam[0]) = drvQEl;
- *((UInt32 *) &pb.csParam[2]) = firstBlock;
- *((UInt32 *) &pb.csParam[4]) = sizeInBlocks;
-
- err = PBControlSync((ParmBlkPtr) &pb);
- }
-
- // In the debug build, check that our changes stuck.
-
- #if MORE_DEBUG
- if (err == noErr) {
- OSErr debugErr;
- UInt32 trueFirstBlock;
- UInt32 trueSizeInBlocks;
-
- debugErr = MoreGetDrivePartition(drive, &trueFirstBlock, &trueSizeInBlocks);
- MoreAssertQ(debugErr == noErr && trueSizeInBlocks == sizeInBlocks && trueFirstBlock == firstBlock);
- }
- #endif
-
- return err;
- }
-
- extern pascal OSErr MoreGetDrivePartition(SInt16 drive, UInt32 *firstBlock, UInt32 *sizeInBlocks)
- // See comment in interface part.
- {
- OSErr err;
- partInfoRec partInfo;
-
- MoreAssertQ(drive > 0);
- MoreAssertQ(firstBlock != nil);
- MoreAssertQ(sizeInBlocks != nil);
-
- err = MoreGetPartitionInfo(drive, &partInfo);
- if (err == noErr) {
- *firstBlock = partInfo.physPartitionLoc;
- err = MoreGetDriveSize(drive, sizeInBlocks);
- }
- return err;
- }
-
- extern pascal OSErr MoreGetPartitionInfo(SInt16 drive, partInfoRec *partInfo)
- // See comment in interface part.
- {
- OSErr err;
- OSErr junk;
- CntrlParam pb;
-
- MoreAssertQ(drive > 0);
- MoreAssertQ(partInfo != nil);
-
- // First check that the driver supports the File Exchange
- // control call interface.
-
- err = noErr;
- if ( ! MoreDriveSupportFileExchange(drive) ) {
- err = controlErr;
- }
-
- // Make the kGetADrive call to the driver. Because
- // we pass a pointer to memory outside of the parameter
- // block (partInfo) and the driver might be a paging device,
- // we must hold partInfo (and make sure to unhold it later!).
-
- if (err == noErr) {
- err = SafeHoldMemory(partInfo, sizeof(*partInfo));
- if (err == noErr) {
- pb.ioVRefNum = drive;
- pb.ioCRefNum = MoreGetDriveRefNum(drive);
- pb.csCode = kGetPartInfo;
- *((partInfoRec **) &pb.csParam[0]) = partInfo;
-
- err = PBStatusSync((ParmBlkPtr) &pb);
-
- junk = SafeUnholdMemory(partInfo, sizeof(*partInfo));
- MoreAssertQ(junk == noErr);
- }
- }
-
- return err;
- }
-
- #if 0
-
- // •••
- // This code temporarily disable while I figure out what's going on.
- // -- Quinn, 3 Mar 1999
-
- extern pascal OSErr MoreGetPartitionVolume(DriverRefNum refNum, const partInfoRec *partInfo, SInt16 *vRefNum)
- // See comment in interface part.
- {
- OSErr err;
- OSErr junk;
- CntrlParam pb;
-
- MoreAssertQ(refNum < 0);
- MoreAssertQ(partInfo != nil);
- MoreAssertQ(vRefNum != nil);
-
- // First check that the driver supports the File Exchange
- // control call interface.
-
- err = noErr;
- if ( ! MoreDriveSupportFileExchangeInternal(refNum, 0) ) {
- err = controlErr;
- }
-
- // Make the kGetPartitionStatus call to the driver. Because
- // we pass a pointer to memory outside of the parameter
- // block (partInfo and vRefNum) and the driver might be a paging device,
- // we must hold that memory (and make sure to unhold it later!).
-
- if (err == noErr) {
- err = SafeHoldMemory( (partInfoRec *) partInfo, sizeof(*partInfo));
- if (err == noErr) {
- err = SafeHoldMemory(vRefNum, sizeof(*vRefNum));
- if (err == noErr) {
- pb.ioVRefNum = 0;
- pb.ioCRefNum = refNum;
- pb.csCode = kGetPartitionStatus;
- *((partInfoRec **) &pb.csParam[0]) = (partInfoRec *) partInfo;
- *((SInt16 **) &pb.csParam[2]) = vRefNum;
-
- err = PBStatusSync((ParmBlkPtr) &pb);
-
- junk = SafeUnholdMemory(vRefNum, sizeof(*vRefNum));
- MoreAssertQ(junk == noErr);
- }
- junk = SafeUnholdMemory( (partInfoRec *) partInfo, sizeof(*partInfo));
- MoreAssertQ(junk == noErr);
- }
- }
- return err;
- }
-
- #endif
-